package filter

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"github.com/tealeg/xlsx/v3"
	"go.manyji.com/lib/config"
	"go.manyji.com/lib/encoding/json"
	"go.manyji.com/lib/log"
	"go.manyji.com/lib/util/convert"
	"io"
	"net/http"
	"os"
	"sieve_proc/constant"
	"sieve_proc/request"
	"sieve_proc/util"
	"strings"
	"time"
)

var (
	shuju008_url      = config.String("filter.shuju008.url", "https://www.shuju008.com/ot/filter_live.jsp", "url")
	shuju008_account  = config.String("filter.shuju008.account", "xuan01", "account")
	shuju008_key      = config.String("filter.shuju008.key", "695fa8717cce1e36ff099e47902171f8", "key")
	shuju008_taskName = config.String("filter.shuju008.taskname", "xuan01_test_channel_us", "task name")
)

type shuju008 struct {
	Url string
}

type shuju008_responese struct {
	Code int             `json:"returnCode"`
	Msg  string          `json:"returnMsg"`
	Data json.RawMessage `json:"data"`
}

func new_shuju008() *shuju008 {
	return &shuju008{
		Url: fmt.Sprintf("%v?act=%v&credentials=%v", *shuju008_url, *shuju008_account, *shuju008_key),
	}
}
func (this *shuju008) uploadTask(operator, country string, list []string, activeType int) (string, error) {
	content, err := request.HttpUploadFile(this.Url+"&operator="+operator, map[string]string{
		"taskNumName": *shuju008_taskName,
		"country":     country,
		"activeType":  convert.ToString(activeType),
	}, fmt.Sprintf("%s_file_%v.txt", operator, time.Now().UnixNano()), strings.NewReader(strings.Join(list, "\n")))
	if err != nil {
		return "", err
	}
	resp := &shuju008_responese{}
	err = json.Unmarshal(content, &resp)
	if err != nil {
		return "", err
	}
	if resp.Code != 200 {
		return "", fmt.Errorf(resp.Msg)
	}
	return strings.Trim(string(resp.Data), "\""), nil
}
func (this *shuju008) UploadWsFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("filter", country, list, 0)
}
func (this *shuju008) UploadWsActiveTask(active int, country string, list []string) (string, error) {

	/*
		活跃类型需加入这个参数，0--频繁上线，1---最近上线，2--近期上线
		传入 3-7-15 三个值其中一个，如不传则默认 15 天（区间活跃填写）
	*/
	activeValue := -1
	operator := "live"
	if v, ok := map[int]int{
		0: 3,
		1: 7,
		2: 15,
	}[active]; ok {
		activeValue = v
		operator = "generalLive"
	}

	return this.uploadTask(operator, country, list, activeValue)
}

func (this *shuju008) UploadWsSexTask(country string, list []string) (string, error) {
	return this.uploadTask("gender", country, list, 0)
}

func (this *shuju008) UploadFBFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("fb", country, list, 0)
}

func (this *shuju008) UploadINSFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("ins", country, list, 0)
}

func (this *shuju008) UploadLinkFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("linkedin", country, list, 0)
}

func (this *shuju008) UploadLinkSexFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("linkedinSenior", country, list, 0)
}

func (this *shuju008) UploadLinkStandardFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("linkedinGender", country, list, 0)
}

func (this *shuju008) UploadViberFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("viber", country, list, 0)
}

func (this *shuju008) UploadViberActiveTask(active int, country string, list []string) (string, error) {
	return this.uploadTask("viberActive", country, list, active)
}

func (this *shuju008) UploadAmazonFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("amazon", country, list, 0)
}

func (this *shuju008) UploadLineFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("line", country, list, 0)
}

func (this *shuju008) UploadLineSexTask(country string, list []string) (string, error) {
	return this.uploadTask("lineGender", country, list, 0)
}

func (this *shuju008) UploadZaloFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("zalo", country, list, 0)
}

func (this *shuju008) UploadZaloSexTask(country string, list []string) (string, error) {
	return this.uploadTask("zaloGender", country, list, 0)
}

func (this *shuju008) UploadTwitterFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("twitter", country, list, 0)
}

func (this *shuju008) UploadSkypeFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("skype", country, list, 0)
}

func (this *shuju008) UploadTgFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("tg", country, list, 0)
}

func (this *shuju008) UploadTgActiveTask(active int, country string, list []string) (string, error) {
	return this.uploadTask("tgActive", country, list, active)
}

func (this *shuju008) UploadMomoFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("momo", country, list, 0)
}

func (this *shuju008) UploadMtnMomoFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("mtnMomo", country, list, 0)
}

func (this *shuju008) UploadMicrosoftFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("microsoft", country, list, 0)
}

func (this *shuju008) UploadTrueMoneyFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("trueMoney", country, list, 0)
}

func (this *shuju008) UploadBotIMFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("botim", country, list, 0)
}

func (this *shuju008) UploadOperatorStandardFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("operatorStandard", country, list, 0)
}

func (this *shuju008) UploadOperatorPremiumFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("operatorPremium", country, list, 0)
}

func (this *shuju008) UploadPhoneCheckFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("phoneCheck", country, list, 0)
}

func (this *shuju008) UploadTkFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("kk", country, list, 0)
}

func (this *shuju008) UploadTkStandardFilterTask(country string, list []string) (string, error) {
	return this.uploadTask("kkGender", country, list, 0)
}

type shuju008_responese_data struct {
	State         string      `json:"state"`
	Count         interface{} `json:"count"`
	CompleteCount interface{} `json:"completeCount"`
	Okcount       interface{} `json:"okcount"`
}

// {"returnCode":200,"returnMsg":"success","data":{"okcount":"0","count":1300,"state":"running","completeCount":"0","taskId":"0529-tgActive-xuan01_test_channel_us-1685348610034-IN-212"}}
func (this *shuju008) CheckWsFilterTask(taskId string) (string, error) {
	content, err := request.Get(fmt.Sprintf("%s&operator=search&taskId=%v", this.Url, taskId))
	log.Info("CheckWsFilterTask:", content)
	if err != nil {
		return "", err
	}
	resp := &shuju008_responese{}
	err = json.Unmarshal([]byte(content), &resp)
	if err != nil {
		return "", err
	}
	if resp.Code != 200 {
		return "", fmt.Errorf(resp.Msg)
	}
	retData := &shuju008_responese_data{}
	err = json.Unmarshal(resp.Data, &retData)
	if err != nil {
		return "", err
	}
	s, _ := json.Marshal(&FilterCheckTaskData{
		Status: retData.State,
		TCnt:   convert.Atoi(fmt.Sprintf("%v", retData.Count)),
		FCnt:   convert.Atoi(fmt.Sprintf("%v", retData.CompleteCount)),
		SCnt:   convert.Atoi(fmt.Sprintf("%v", retData.Okcount)),
	})
	return string(s), nil
}

func (this *shuju008) DownFilterTask(tag, taskId string) ([]string, error) {
	content, err := request.Get(fmt.Sprintf("%s&operator=down&taskId=%s", this.Url, taskId))
	log.Info(fmt.Sprintf("%s&operator=down&taskId=%s,retData:=%s", this.Url, taskId, content))
	if err != nil {
		return nil, err
	}
	var (
		phones       []string
		nonFilterMap = map[string]struct{}{
			constant.OperatorStandardTag: {},
			constant.OperatorPremiumTag:  {},
		}
	)

	util.LoadLines(strings.NewReader(content), func(line string) error {

		if _, ok := nonFilterMap[tag]; ok {
			phones = append(phones, line)
			return nil
		}

		line = strings.Split(strings.Split(line, ":")[0], "_")[0]
		phones = append(phones, line)
		return nil
	})
	return phones, nil
}

func (this *shuju008) DownSexFilterTask(tag, taskId string, xlsxOutFile string, csvOutFile string) ([]string, error) {

	var (
		operatorMap = map[string]string{
			constant.WsaTag:              "excelDown",
			constant.TgaTag:              "tgActiveDown",
			constant.LineTag:             "lineDown",
			constant.LinksTag:            "linkedinSeniorDown",
			constant.LinkStandardTag:     "linkedinGenderDown",
			constant.ViberfTag:           "viberDown",
			constant.ViberaTag:           "viberActiveDown",
			constant.WssTag:              "genderDown",
			constant.LinesTag:            "lineGenderDown",
			constant.ZalosTag:            "zaloGenderDown",
			constant.OperatorStandardTag: "operatorStandardDown",
			constant.OperatorPremiumTag:  "operatorPremiumDown",
			constant.TkTag:               "excelCSV",
			constant.TkStandardTag:       "excelCSV",
		}
		operator = "excelDown"
	)

	// 非这些类型的不需要导出excel类型
	if v, ok := operatorMap[tag]; ok {
		operator = v
	}

	return DownSexSieveFile(operator, taskId, this.Url, xlsxOutFile, csvOutFile)
}

func CloneReadCloser(content []byte) (io.ReadCloser, io.ReadCloser, error) {
	// 读取原始数据流的内容

	// 创建一个新的读取器，用于提供克隆后的数据流
	r1 := io.NopCloser(bytes.NewReader(content))

	// 创建一个新的读取器，用于提供原始数据流
	r2 := io.NopCloser(bytes.NewReader(content))

	return r1, r2, nil
}

func CSVBytesToCSV(infileBody []byte, outFile string) error {

	fWriter, err := os.Create(outFile)
	if err != nil {
		return err
	}
	rWriter := csv.NewWriter(fWriter)
	// 使用strings.NewReader将字节流转换为Reader
	reader := csv.NewReader(bytes.NewReader(infileBody))
	defer rWriter.Flush()

	// 逐行读取字节流数据并写入CSV文件
	for {
		record, err := reader.Read()
		if err != nil {
			if err == io.EOF {
				break
			}
			return err
		}

		if err := rWriter.Write(record); err != nil {
			return err
		}
	}

	return nil
}

func ExcelBytesToCSV(infileBody []byte, outFile string) error {

	f, err := xlsx.OpenBinary(infileBody)
	if err != nil {
		return err
	}

	fWriter, err := os.Create(outFile)
	if err != nil {
		return err
	}
	rWriter := csv.NewWriter(fWriter)

	defer fWriter.Close()
	sh, ok := f.Sheet["Sheet1"]
	if ok {
		err = sh.ForEachRow(func(r *xlsx.Row) error {

			var cells []string
			_ = r.ForEachCell(func(c *xlsx.Cell) error {
				value, err := c.FormattedValue()
				cells = append(cells, value)
				return err
			})

			return rWriter.Write(cells)
		})
	}

	rWriter.Flush()

	return err
}

func DownSexSieveFile(operator string, taskId, url string, xlsxOutFile, csvOutFile string) ([]string, error) {

	url = fmt.Sprintf("%v&operator=%v&taskId=%v", url, operator, taskId)
	method := "GET"

	client := &http.Client{}
	req, err := http.NewRequest(method, url, nil)

	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	defer res.Body.Close()

	content, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	if xlsxOutFile != "" && isExcel(content) {
		out, err := os.Create(xlsxOutFile)
		if err != nil {
			return nil, err
		}

		defer out.Close()
		cloneBody := io.NopCloser(bytes.NewReader(content))
		_, err = io.Copy(out, cloneBody)
	}

	if csvOutFile != "" {
		//if isExcel(content) {
		//	err = ExcelBytesToCSV(content, csvOutFile)
		//} else {
		//	if isCSV(content) {
		//		err = CSVBytesToCSV(content, csvOutFile)
		//	}
		//}

		if err := ExcelBytesToCSV(content, csvOutFile); err != nil {
			log.Err(fmt.Sprintf("%v %v %v %v bytes to csv1 failed:%+v", isCSV(content), isExcel(content), taskId, operator, err))
			err = CSVBytesToCSV(content, csvOutFile)
			log.Err(fmt.Sprintf("%v %v %v %v bytes to csv2 failed:%+v", isCSV(content), isExcel(content), taskId, operator, err))
		}
	}

	return nil, err
}

func isCSV(data []byte) bool {
	// CSV文件通常是纯文本文件，没有特定的文件头标识。
	// 在这个示例中，我们简单地检查数据中是否包含逗号分隔符。
	return bytes.Contains(data, []byte(","))
}

func isExcel(data []byte) bool {
	// Excel文件通常以特定的文件头标识开头。
	// 例如，.xlsx文件的标识是"PK"，.xls文件的标识是"D0CF11E0"。
	// 在这个示例中，我们检查文件头是否以"PK"开头。
	return bytes.HasPrefix(data, []byte("PK")) || bytes.HasPrefix(data, []byte("D0CF11E0"))
}
